﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using Excel;

namespace OrderAggregator.InputFormats.Excel
{
    /// <summary>
    /// Парсер заявок в Excel форматер
    /// </summary>
	public class ExcelFormat : BaseExcelFormat
    {
        public readonly static ExcelFormat Instance = new ExcelFormat();

        private ExcelFormat()
        {
        }

        /// <summary>
        /// Parse
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="getStream"></param>
        /// <returns></returns>
        public override BaseOrder Parse(string filePath, Func<System.IO.Stream> getStream)
        {
            if (!string.Equals(System.IO.Path.GetExtension(filePath), dotXLS, StringComparison.InvariantCultureIgnoreCase))
            {
                return null;
            }


            var dataSet = ReadAsDataset(getStream());

            if (dataSet == null || dataSet.Tables.Count == 0)
			{
				return null;
			}

        	var table = dataSet.Tables[0];
                       
            var order = new BaseOrder();

            order.Id = GetCell<string>(table, 1, 5);

			if (string.IsNullOrEmpty(order.Id))
			{
				return null;
			}

        	{
                string client = GetCell<string>(table, 2, 2);

				if (string.IsNullOrEmpty(client))
				{
					return null;
				}
				
				int sepPos = client.IndexOf(',');
                if (sepPos > 0)
                {
                    client = client.Substring(0, sepPos);
                    client += ", " + GetCell<string>(table, 1, 2);
                }
                order.Client = client;
            }

            order.ReceiveTime = GetCell<DateTime>(table, 4, 2);

            int startRow, endRow;
            GetOrderRowsRange(table, out startRow, out endRow);
                       
            
            for (int row = startRow; row <= endRow; row++)
            {
                var orderRow = new BaseOrderRow();

                orderRow.ProductName = GetCell<string>(table, row, 2);
                orderRow.ProductCode = GetCell<string>(table, row, 11);
                orderRow.Count = GetCell<decimal>(table, row, 4);
                
                order.OrderRows.Add(orderRow);
            }

            return order;
        }

        private const string NameColumn = "Наименование товара";
        private const string TotalRowsText = "Итого мест:";

        private static void GetOrderRowsRange(DataTable table, out int startRow, out int endRow)
        {
            CheckCell(table, 5, 2, NameColumn);
            
            startRow = 6;

            string str;

            var rowsCount = table.Rows.Count;

            for (endRow = startRow; endRow <= rowsCount; endRow++)
            {
                str = GetCell<string>(table, endRow, 2);

                if (string.IsNullOrEmpty(str) ||
                    string.IsNullOrEmpty(str.Trim())
                    )
                {
                    break;
                }
            }

            CheckCell(table, endRow - 4, 2, TotalRowsText);
            endRow -= 5;
        }

        private static T GetCell<T>(DataTable table, int row, int column)
        {
            if (table.Rows.Count == 0)
            {
                return default(T);
            }

            var obj = table.Rows[row - 1][column - 1];

            if (obj is T)
            {
                return (T)obj;
            }

            if (typeof(T) == typeof(string))
            {
                if (obj is DBNull || obj == null)
                {
                    return default(T);
                }

                return (T)(object)obj.ToString();
            }
            else if (typeof(T) == typeof(DateTime))
            {
                return (T)(object)DateTime.FromOADate(Convert.ToDouble(obj));
            }

            return (T)Convert.ChangeType(obj, typeof(T));
        }

        private static void CheckCell(
            DataTable table,
            int row, int column,
            string expectedText
            )
        {
            var str = GetCell<string>(table, row, column);

            if (str != expectedText)
            {

                throw new Exception(
                    string.Format("Неверный формат заявки: в [{0};{1}] ожидалось \"{2}\"",
                                  row, column,
                                  expectedText
                        )
                    );
            }
        }
    }
}
